package org.swift.mashup.engine.deployment;

import java.util.Arrays;
import java.util.Collection;

import javax.xml.namespace.QName;

import org.apache.axis2.Constants;
import org.apache.axis2.description.AxisMessage;
import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.log4j.Logger;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.swift.commons.deployer.framework.schema.SchemaGenerator;
import org.swift.mashup.engine.model.Mashup;

public class MashupSchemaGenerator extends SchemaGenerator {
	public static String MASHUP_PROPERTY = "org.swift.mashup.engine.deployment.MashupSchemaGenerator#MASHUP_PROPERTY";

	private static Logger log = Logger.getLogger(MashupDeployer.class);
	private String targetNamespace;
	private String namespacePrefix;
	

	public MashupSchemaGenerator(String targetNamespace, String namespacePrefix) {
		this.targetNamespace = targetNamespace;
		this.namespacePrefix = namespacePrefix;
	}

	@SuppressWarnings("unchecked")	
	public void generateSchema(AxisService axisService) {
		super.generateSchema(axisService);
		axisService.getNamespaceMap().put(Constants.NS_PREFIX_SCHEMA_XSD,
				Constants.URI_2001_SCHEMA_XSD);
	}
	
	protected void generateSchemaforOperation(AxisOperation axisOperation) {
		XmlSchema schema = getXmlSchema(this.targetNamespace);
		QName opElName = getOperationParameterElementName(axisOperation
				.getName());
		XmlSchemaComplexType inputComplexType = getComplexTypeForElement(
				schema, opElName);
		if (inputComplexType == null) {
			log.debug("No input elements defined yet for this mashup.  Generating complex type.");
			
			Mashup mashup = (Mashup) axisOperation.getAxisService()
					.getParameter(MASHUP_PROPERTY).getValue();
			if ("runConcurrent".equals(axisOperation.getName().getLocalPart())) {
				addParametersSchemaType(schema, Arrays.asList("processid", "sessionid", "workingDirectory", "startIndex", "endIndex"), opElName);
			} else {				
				addParametersSchemaType(schema, mashup.getParameterNames(),
						opElName);
			}
		} else if (log.isDebugEnabled()) {
			log.debug("Input elements found.  No need to generate them again.");
		}
		assignOperationMessages(axisOperation, opElName);
	}

	protected QName getOperationParameterElementName(QName operationName) {
		return new QName(targetNamespace, operationName.getLocalPart() + "Request",
				namespacePrefix);
	}

	protected void assignOperationMessages(AxisOperation axisOperation,
			QName element) {
		AxisMessage message = axisOperation
				.getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
		message.setName(axisOperation.getName().getLocalPart() + "MessageIn");
		message.setElementQName(element);

		message = axisOperation
				.getMessage(WSDLConstants.MESSAGE_LABEL_OUT_VALUE);
		message.setName(axisOperation.getName().getLocalPart() + "MessageOut");
		message.setElementQName(new QName(org.apache.axis2.namespace.Constants.URI_DEFAULT_SCHEMA_XSD, "anyType","xs"));
	}
	
	protected String getDefaultNamespace() {
		return this.targetNamespace;
	}
	
	protected String getDefaultNamespacePrefix() {
		return this.namespacePrefix;
	}

	protected QName addParametersSchemaType(XmlSchema xmlSchema,
			Collection<String> parameters, QName elementQName) {
		XmlSchemaComplexType complexType = new XmlSchemaComplexType(xmlSchema);
		XmlSchemaSequence particle = new XmlSchemaSequence();
		for (String parameter : parameters) {
			XmlSchemaElement el = new XmlSchemaElement();
			el.setName(parameter);
			el.setNillable(false);
			el.setMaxOccurs(1);
			el.setMinOccurs(0);
			el.setSchemaTypeName(Constants.XSD_STRING);
			particle.getItems().add(el);
		}
		complexType.setParticle(particle);
		XmlSchemaElement element = new XmlSchemaElement();
		element.setName(elementQName.getLocalPart());
		element.setSchemaType(complexType);
		element.setQName(elementQName);
		xmlSchema.getItems().add(element);
		xmlSchema.getElements().add(elementQName, element);
		return elementQName;
	}

	@SuppressWarnings("unchecked")
	protected XmlSchema getTargetSchema(AxisService service) {
		for (XmlSchema schema : (Collection<XmlSchema>) service.getSchema()) {
			if (getTargetNamespace().equals(schema.getTargetNamespace())) {
				return schema;
			}
		}
		return null;
	}

	public String getTargetNamespace() {
		return targetNamespace;
	}

	public void setTargetNamespace(String targetNamespace) {
		this.targetNamespace = targetNamespace;
	}

	public String getNamespacePrefix() {
		return namespacePrefix;
	}

	public void setNamespacePrefix(String namespacePrefix) {
		this.namespacePrefix = namespacePrefix;
	}

}
